package service

import (
	"common/biz"
	"common/logs"
	"common/utils"
	"context"
	"core/dao"
	"core/models/db"
	"core/models/entity"
	"core/repo"
	"fmt"
	"framework/msError"
	"hall/models/request"
	hall "hall/models/response"
	"time"
)

type ProductionService struct {
	productionDao *dao.ProductionDao
	warehouseDao  *dao.WarehouseDao
	configDao     *dao.ConfigDao
	typeDao       *dao.TypeDao
}

func (s *ProductionService) ProductionList(ctx context.Context, uid uint) *[]hall.ProductionList {
	data := s.productionDao.ProductionList(ctx)
	productions := make([]hall.ProductionList, 0)
	types := s.typeDao.GetInfo(ctx, 11)
	for _, v := range *data {
		roleProduction := s.productionDao.GetRoleProduction(ctx, uid, v.ID)
		var isHave bool
		if roleProduction != nil && roleProduction.ID != 0 {
			isHave = true
		}
		production := hall.ProductionList{
			ID:                v.ID,
			Name:              v.Name,
			ConsumeTypeID:     v.ConsumeTypeID,
			ConsumeTypeName:   v.Types.Name,
			ConsumeTypeImg:    v.Types.Img,
			ConsumeTypeModule: v.Types.Module,
			ConsumeNum:        utils.DecimalIntDiv(v.ConsumeNum, types.Val),
			IsHave:            isHave,
			TotalOutput:       utils.DecimalIntDiv(roleProduction.TotalOutput, types.Val),
			SyOutput:          utils.DecimalIntDiv(roleProduction.SyOutput, types.Val),
			Status:            roleProduction.Status,
			StarTime:          roleProduction.StarTime,
			EndTime:           roleProduction.EndTime,
			Output:            utils.DecimalIntDiv(roleProduction.Output, types.Val),
			Receive:           utils.DecimalIntDiv(roleProduction.Receive, types.Val),
		}
		if roleProduction.CardWID != 0 {
			card := s.warehouseDao.GetCardInfo(ctx, uid, roleProduction.CardWID)
			production.CardTypeName = card.Types.Name
			production.CardTypeImg = card.Types.Img
			production.CardTypeDescImg = card.Types.DescImg
			production.CardTypeModule = card.Types.Module
			production.CardLevel = card.TypeCard.Level
		}
		if roleProduction.EquipOneWID != 0 {
			equipOne := s.warehouseDao.GetEquipInfo(ctx, uid, roleProduction.EquipOneWID)
			production.EquipOneTypeName = equipOne.Types.Name
			production.EquipOneTypeImg = equipOne.Types.Img
			production.EquipOneTypeModule = equipOne.Types.Module
		}
		if roleProduction.EquipTwoWID != 0 {
			equipTwo := s.warehouseDao.GetEquipInfo(ctx, uid, roleProduction.EquipTwoWID)
			production.EquipTwoTypeName = equipTwo.Types.Name
			production.EquipTwoTypeImg = equipTwo.Types.Img
			production.EquipTwoTypeModule = equipTwo.Types.Module
		}

		productions = append(productions, production)
	}
	return &productions
}

func (s *ProductionService) RoleProductionInfo(ctx context.Context, uid uint, id uint) *db.RoleProduction {
	return s.productionDao.GetRoleProduction(ctx, uid, id)
}

func (s *ProductionService) ProductionOpen(ctx context.Context, uid, id uint) *msError.Error {
	roleProduction := s.RoleProductionInfo(ctx, uid, id)
	if roleProduction != nil && roleProduction.ID != 0 {
		return biz.ProductionNotOpenExist
	}
	if id > 1 {
		beforeRoleProduction := s.RoleProductionInfo(ctx, uid, id-1)
		if beforeRoleProduction.ID == 0 {
			return biz.ProductionNotBeforeOpen
		}
	}
	production := s.productionDao.GetProductionInfo(ctx, id)
	return s.productionDao.ProductionOpen(ctx, uid, production)
}

func (s *ProductionService) ProductionInstallOfDisassembly(ctx context.Context, uid uint, req *request.ProductionInstallOfDisassembly) *msError.Error {
	roleProduction := s.productionDao.GetRoleProduction(ctx, uid, req.ID)
	if roleProduction == nil && roleProduction.ID == 0 {
		logs.Error("[productionHandler]用户%d ProductionInstallOfDisassembly err:未开通槽", uid, req)
		return biz.ProductionNotOpen
	}
	if roleProduction.Status {
		logs.Error("[productionHandler]用户%d ProductionInstallOfDisassembly err:该槽位已开始生产", uid, roleProduction)
		return biz.ProductionAlreadyProduction
	}
	return s.productionDao.ProductionInstallOfDisassembly(ctx, uid, req, roleProduction)
}

func (s *ProductionService) ProductionStart(ctx context.Context, data *db.RoleProduction) (*hall.ProductionList, *entity.ProductionOutput, *msError.Error) {
	productionOutput := &entity.ProductionOutput{
		UID:              data.UID,
		ProductionID:     data.ProductionID,
		RoleProductionID: data.ID,
		CardWID:          data.CardWID,
		EquipOneWID:      data.EquipOneWID,
		EquipTwoWID:      data.EquipTwoWID,
		TotalOutput:      utils.DecimalIntByFloat(data.TotalOutput),
		SyOutput:         utils.DecimalIntByFloat(data.SyOutput),
	}
	data.Status = true
	card := s.warehouseDao.GetCardInfo(ctx, data.UID, data.CardWID)
	if card == nil || card.ID == 0 {
		logs.Error("[productionHandler]用户%d ProductionStart err:卡牌不存在,req=%v", data.UID, data)
		return nil, nil, biz.RequestDataError
	}
	productionOutput.CardTypeID = card.TypeID
	productionOutput.CardTypeName = card.Types.Name
	productionOutput.CardTypeImg = card.Types.Img
	productionOutput.CardTypeDescImg = card.Types.DescImg
	productionOutput.CardTypeModule = card.Types.Module
	productionOutput.CardLevel = card.TypeCard.Level
	productionOutput.CardOutput = utils.DecimalIntByFloat(card.Rate)
	var increase1, increase2 float64
	if data.EquipOneWID != 0 {
		equip := s.warehouseDao.GetEquipInfo(ctx, data.UID, data.EquipOneWID)
		if equip == nil || equip.ID == 0 {
			logs.Error("[productionHandler]用户%d ProductionStart err:装备1不存在,req=%v", data.UID, data)
			return nil, nil, biz.RequestDataError
		}
		increase1 = utils.DecimalFloatMul(productionOutput.CardOutput, utils.DecimalIntDiv(equip.Conf.Increase, 100))
		productionOutput.EquipOneTypeID = equip.TypeID
		productionOutput.EquipOneTypeName = equip.Types.Name
		productionOutput.EquipOneTypeImg = equip.Types.Img
		productionOutput.EquipOneTypeModule = equip.Types.Module
		productionOutput.EquipOneSyCount = equip.SyNum
		productionOutput.EquipOneOutput = increase1
	}
	if data.EquipTwoWID != 0 {
		equip := s.warehouseDao.GetEquipInfo(ctx, data.UID, data.EquipTwoWID)
		if equip == nil || equip.ID == 0 {
			logs.Error("[productionHandler]用户%d ProductionStart err:装备2不存在,req=%v", data.UID, data)
			return nil, nil, biz.RequestDataError
		}
		increase2 = utils.DecimalFloatMul(productionOutput.CardOutput, utils.DecimalIntDiv(equip.Conf.Increase, 100))
		productionOutput.EquipTwoTypeID = equip.TypeID
		productionOutput.EquipTwoTypeName = equip.Types.Name
		productionOutput.EquipTwoTypeImg = equip.Types.Img
		productionOutput.EquipTwoTypeModule = equip.Types.Module
		productionOutput.EquipTwoSyCount = equip.SyNum
		productionOutput.EquipTwoOutput = increase2
	}
	data.Output = utils.DecimalFloatByInt(utils.DecimalFloatAdd(productionOutput.CardOutput, utils.DecimalFloatAdd(productionOutput.EquipOneOutput, productionOutput.EquipTwoOutput)))
	data.StarTime = time.Now().Unix()
	var syTime int64 = 86400
	if data.SyOutput < data.Output {
		eachSecondOutput := utils.DecimalIntDiv(syTime, data.Output)
		syTime = utils.DecimalFloatByInt(utils.DecimalFloatMul(utils.DecimalIntByFloat(data.SyOutput), eachSecondOutput))
		data.Output = data.SyOutput
	}
	data.EndTime = data.StarTime + syTime
	productionOutput.Output = utils.DecimalIntByFloat(data.Output)
	productionOutput.StarTime = data.StarTime
	productionOutput.EndTime = data.EndTime
	err := s.productionDao.ProductionStart(ctx, data, productionOutput)
	production := s.productionDao.GetProductionInfo(ctx, data.ProductionID)
	types := s.typeDao.GetInfo(ctx, 11)
	info := &hall.ProductionList{
		ID:                 data.ProductionID,
		Name:               production.Name,
		ConsumeTypeID:      production.ConsumeTypeID,
		ConsumeTypeName:    production.Types.Name,
		ConsumeTypeImg:     production.Types.Img,
		ConsumeTypeModule:  production.Types.Module,
		ConsumeNum:         utils.DecimalIntDiv(production.ConsumeNum, types.Val),
		IsHave:             true,
		CardTypeName:       productionOutput.CardTypeName,
		CardTypeImg:        productionOutput.CardTypeImg,
		CardTypeDescImg:    productionOutput.CardTypeDescImg,
		CardTypeModule:     productionOutput.CardTypeModule,
		CardLevel:          productionOutput.CardLevel,
		EquipOneTypeName:   productionOutput.EquipOneTypeName,
		EquipOneTypeImg:    productionOutput.EquipOneTypeImg,
		EquipOneTypeModule: productionOutput.EquipOneTypeModule,
		EquipTwoTypeName:   productionOutput.EquipTwoTypeName,
		EquipTwoTypeImg:    productionOutput.EquipTwoTypeImg,
		EquipTwoTypeModule: productionOutput.EquipTwoTypeModule,
		TotalOutput:        utils.DecimalIntDiv(data.TotalOutput, types.Val),
		SyOutput:           utils.DecimalIntDiv(data.SyOutput, types.Val),
		Status:             true,
		StarTime:           data.StarTime,
		EndTime:            data.EndTime,
		Output:             utils.DecimalIntDiv(data.Output, types.Val),
		Receive:            utils.DecimalIntDiv(data.Receive, types.Val),
	}
	return info, productionOutput, err
}

func (s *ProductionService) ProductionEnd(ctx context.Context, productionOutput *entity.ProductionOutput) (*hall.ProductionList, error) {
	var err error
	data := s.RoleProductionInfo(ctx, productionOutput.UID, productionOutput.ProductionID)
	if data == nil || data.ID == 0 {
		err = fmt.Errorf("[productionHandler]用户%d ProductionEnd err:生产槽位不存在,req=%v", productionOutput.UID, productionOutput)
		return nil, err
	}
	if !data.Status {
		err = fmt.Errorf("[productionHandler]用户%d ProductionEnd err:生产槽位未开始,req=%v", productionOutput.UID, productionOutput)
		return nil, err
	}
	err = s.productionDao.ProductionEnd(ctx, data, productionOutput)
	production := s.productionDao.GetProductionInfo(ctx, data.ProductionID)
	types := s.typeDao.GetInfo(ctx, 11)
	info := &hall.ProductionList{
		ID:                 data.ProductionID,
		Name:               production.Name,
		ConsumeTypeID:      production.ConsumeTypeID,
		ConsumeTypeName:    production.Types.Name,
		ConsumeTypeImg:     production.Types.Img,
		ConsumeTypeModule:  production.Types.Module,
		ConsumeNum:         utils.DecimalIntDiv(production.ConsumeNum, types.Val),
		IsHave:             true,
		CardTypeName:       productionOutput.CardTypeName,
		CardTypeImg:        productionOutput.CardTypeImg,
		CardTypeModule:     productionOutput.CardTypeModule,
		EquipOneTypeName:   productionOutput.EquipOneTypeName,
		EquipOneTypeImg:    productionOutput.EquipOneTypeImg,
		EquipOneTypeModule: productionOutput.EquipOneTypeModule,
		EquipTwoTypeName:   productionOutput.EquipTwoTypeName,
		EquipTwoTypeImg:    productionOutput.EquipTwoTypeImg,
		EquipTwoTypeModule: productionOutput.EquipTwoTypeModule,
		TotalOutput:        utils.DecimalIntDiv(data.TotalOutput, types.Val),
		SyOutput:           utils.DecimalIntDiv(data.SyOutput, types.Val),
		Status:             false,
		StarTime:           data.StarTime,
		EndTime:            data.EndTime,
		Output:             utils.DecimalIntByFloat(data.Output),
		Receive:            utils.DecimalIntDiv(data.Receive, types.Val),
	}
	return info, err
}

func (s *ProductionService) MongoOutputAll(ctx context.Context) *[]entity.ProductionOutput {
	return s.productionDao.MongoOutputAll(ctx)
}

func (s *ProductionService) ProductionReceive(ctx context.Context, uid uint, id uint) *msError.Error {
	data := s.RoleProductionInfo(ctx, uid, id)
	if data.Receive <= 0 {
		return nil
	}
	return s.productionDao.ProductionReceive(ctx, data)
}

func NewProductionService(r *repo.Manager) *ProductionService {
	return &ProductionService{
		productionDao: dao.NewProductionDao(r),
		warehouseDao:  dao.NewWarehouseDao(r),
		configDao:     dao.NewConfigDao(r),
		typeDao:       dao.NewTypeDao(r),
	}
}
